package com.chatplus.application.web.advice;

import cn.dev33.satoken.exception.NotLoginException;
import com.chatplus.application.common.domain.response.TurnRightResponse;
import com.chatplus.application.common.exception.BadRequestException;
import com.chatplus.application.common.exception.ForbiddenException;
import com.chatplus.application.common.exception.InternalErrorException;
import com.chatplus.application.common.exception.NameEnumNotFoundException;
import com.chatplus.application.common.exception.NoContentException;
import com.chatplus.application.common.exception.NotDevelopedException;
import com.chatplus.application.common.exception.ServiceException;
import com.chatplus.application.common.exception.SystemException;
import com.chatplus.application.common.exception.TurnRightException;
import com.chatplus.application.common.exception.UnauthorizedException;
import com.chatplus.application.common.exception.UpgradeRequiredException;
import com.chatplus.application.common.exception.ValueEnumNotFoundException;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.json.JsonReader;
import feign.FeignException;
import jakarta.servlet.http.HttpServletRequest;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@RestControllerAdvice
public class BaseExceptionAdvice extends ResponseEntityExceptionHandler {
    private final static SouthernQuietLogger log = SouthernQuietLoggerFactory.getLogger(BaseExceptionAdvice.class);

    private JsonReader jsonReader;

    @Autowired
    public void setJsonReader(JsonReader jsonReader) {
        this.jsonReader = jsonReader;
    }

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(TurnRightException.class)
    public TurnRightResponse turnRightExceptionHandler(TurnRightException e) {
        return new TurnRightResponse(e);
    }

    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(NotDevelopedException.class)
    public TurnRightResponse notDevelopedExceptionHandler(NotDevelopedException e) {
        return new TurnRightResponse(e);
    }

    //    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BadRequestException.class)
    public TurnRightResponse badRequestExceptionHandler(BadRequestException e) {
        return new TurnRightResponse(e);
    }

    @ResponseStatus(HttpStatus.NO_CONTENT)
    @ExceptionHandler(NoContentException.class)
    public void noContentException() {
    }

    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(ForbiddenException.class)
    public TurnRightResponse forbiddenException(ForbiddenException e) {
        return new TurnRightResponse(e);
    }

    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler({UnauthorizedException.class})
    public void unauthorizedException() {
    }

    @ResponseStatus(HttpStatus.UPGRADE_REQUIRED)
    @ExceptionHandler(UpgradeRequiredException.class)
    public void upgradeRequiredException() {
    }

    @ExceptionHandler({NotLoginException.class})
    public TurnRightResponse authException(Exception e) {
        // TODO 以后看情况做适当拦截
        TurnRightResponse turnRightResponse = new TurnRightResponse();
        turnRightResponse.setMessage("You should put Authorization in domain headers");
        turnRightResponse.setCode(1);
        return turnRightResponse;
    }

    @ExceptionHandler(FeignException.class)
    public Object feignException(FeignException e) {
        log.message("调用feign接口失败").exception(e).error();
        Object body = e.contentUTF8();
        if (e instanceof FeignException.BadRequest || e instanceof FeignException.Conflict) {
            body = jsonReader.read(e.contentUTF8(), TurnRightResponse.class);
        }
        return new ResponseEntity<>(body, Objects.requireNonNull(HttpStatus.resolve(e.status())));
    }

    @SuppressWarnings("NullableProblems")
//    @Override
//    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
//        headers.setContentType(MediaType.APPLICATION_JSON);
//
//        TurnRightResponse turnRightResponse = new TurnRightResponse();
//        String errorMsg = "参数校验失败";
//        HashMap<String, String> payload = ex.getAllErrors().stream()
//                .collect(Collectors.toMap(ObjectError::getObjectName, ObjectError::toString, (a, b) -> b, HashMap::new));
//        if (CollectionUtils.isNotEmpty(ex.getAllErrors())) {
//            errorMsg = ex.getAllErrors().getFirst().getDefaultMessage();
//        }
//
//        turnRightResponse.setMessage(errorMsg);
//        turnRightResponse.setData(payload);
//
//        return new ResponseEntity<>(turnRightResponse, headers, status);
//    }


    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ConstraintViolationException.class)
    public void constraintViolationException(ConstraintViolationException exception, HttpServletRequest request) {
        log.message("属性校验失败")
                .context("RequestURI", request.getRequestURI())
                .context("violations", exception.getConstraintViolations().stream()
                        .map(v -> v.getPropertyPath().toString() + "=" + v.getMessage())
                        .sorted()
                        .reduce((prev, next) -> prev + "," + next)
                        .map(result -> "[" + result + "]")
                        .orElse("[]"))
                .context("message", exception.getMessage())
                .warn();
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(OptimisticLockingFailureException.class)
    public void optimisticLockingFailureException(OptimisticLockingFailureException e, HttpServletRequest request) {
        log.message("乐观锁并发冲突")
                .context("RequestURI", request.getRequestURI())
                .context("Message", e.getMessage())
                .error();
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(InternalErrorException.class)
    public TurnRightResponse internalErrorExceptionHandler(InternalErrorException e) {
        TurnRightResponse turnRightResponse = new TurnRightResponse();
        turnRightResponse.setMessage(e.getMessage());
        turnRightResponse.setCode(400);
        turnRightResponse.setData(e.getPayload());
        return turnRightResponse;
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = {
            NameEnumNotFoundException.class,
            ServiceException.class,
            SystemException.class,
            ValueEnumNotFoundException.class})
    public TurnRightResponse internalSystemErrorExceptionHandler(RuntimeException e, HttpServletRequest request) {
        log.message("系统内部错误")
                .context("RequestURI", request.getRequestURI())
                .context("Message", e.getMessage())
                .context("e", e.toString())
                .error();
        TurnRightResponse turnRightResponse = new TurnRightResponse();
        turnRightResponse.setMessage(e.getMessage());
        turnRightResponse.setCode(400);
        return turnRightResponse;
    }

    /**
     * 处理 @valid 验证失败异常信息返回
     *
     * @param exception methodArgumentNotValidException
     * @param headers   headers
     * @param status    status
     * @param request   domain
     * @return responseEntity
     */
    @Override
    public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers, @NotNull HttpStatusCode status, @NotNull WebRequest request) {

        headers.setContentType(MediaType.APPLICATION_JSON);
        BindingResult result = exception.getBindingResult();
        Map<String, String> data = new HashMap<>();
        String errorMsg = "参数校验失败";
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            fieldErrors.forEach(error -> data.put(error.getField(), error.getDefaultMessage()));
            //修复errorMsg不是取payload第一条错误消息的问题 by weiride （fieldErrors底层返回的FieldError是乱序的）
            errorMsg = data.values().stream().findFirst().orElse(errorMsg);
        }
        TurnRightResponse response = new TurnRightResponse(new BadRequestException(errorMsg));
        response.setData(data);
        return new ResponseEntity<>(response, headers, status);
    }

    @Override
    protected ResponseEntity<Object> handleTypeMismatch(@NotNull TypeMismatchException ex, HttpHeaders headers, @NotNull HttpStatusCode status, @NotNull WebRequest request) {
        headers.setContentType(MediaType.APPLICATION_JSON);
        TurnRightResponse response = new TurnRightResponse(new BadRequestException("参数不合法"));
        return new ResponseEntity<>(response, headers, status);
    }
}
